//
// Created by Win on 2015/11/24.
//
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#include "frmem.h"
#include "frcomm.h"
#include "frhash2.h"
#include "frreport_comm.h"
#include "frct_comm.h"

#include "dcom_core.h"
#include "dcom_comm.h"


//todo   dcom  bind 链路未解析

/*!
 * dcom 协议判断
 *
 * \p_st_conn			(Conntrack *    连接信息)
 * \p_st_packet			(Apm_packet *	输入数据包)
 *
 * \return				APM_L7_IS_NO: 不是dcom协议\n
 * 						APM_L7_IS_YES:	是dcom协议\n
 */
int dcom_detect(Conntrack *p_st_conn, Apm_packet *p_st_packet)
{
	char *p_data_in = NULL;
	int i_data_len = 0, i_direction = FR_DIRECT_UNKNOWN, i_len_tmp = 0;
	unsigned char uc_packet_type;
	int is_little_endian = 0;

	if(p_st_conn == NULL || p_st_packet == NULL || p_st_packet->p_payload == NULL || p_st_packet->ui_payload_len == 0)
		return APM_L7_IS_NO;

	i_data_len = p_st_packet->ui_payload_len;
	p_data_in = p_st_packet->p_payload;

	if(i_data_len < 2)
		return  APM_L7_IS_NO;

	//version 判断
	if(p_data_in[0] > '\x09' || p_data_in[1] > '\x09')
		return APM_L7_IS_NO;
	
	if(p_data_in[0] == '\x00' && p_data_in[1] == '\x00')
		return APM_L7_IS_NO;

	p_data_in += 2;
	i_data_len -= 2;

	//packet type

	if(i_data_len < 1)
		return APM_L7_IS_NO;

	uc_packet_type = *p_data_in;
	p_data_in+=2;
	i_data_len-=2;

	i_direction = frct_get_direct_known(p_st_conn);
	//判断方向
	if(i_direction == FR_DIRECT_UNKNOWN)
	{
		switch (uc_packet_type)
		{
			//bind
			case '\x0b':
				i_direction = FR_DIRECT_CLIENT2SRV;
				break;

				//bind ack
			case '\x0c':
				i_direction = FR_DIRECT_SRV2CLIENT;
				break;

				//auth3
			case '\x10':
				i_direction = FR_DIRECT_CLIENT2SRV;
				break;

				//request
			case '\x00':
				i_direction = FR_DIRECT_CLIENT2SRV;
				break;

				//response
			case '\x02':
				i_direction = FR_DIRECT_SRV2CLIENT;
				break;

			default:
				return APM_L7_IS_NO;
		}
	}


	if(i_data_len < 7)
		return APM_L7_IS_NO;

	//packet flags    data representation
	if(((unsigned char)p_data_in[0] & 0x10)!= 0)
		is_little_endian = 1;

	p_data_in += 4;

	if(is_little_endian)
		i_data_len = phtons(p_data_in);
	else
		i_data_len = pntohs(p_data_in);

	p_data_in += 2;
	if(is_little_endian)
		i_len_tmp = phtons(p_data_in);
	else
		i_len_tmp = pntohs(p_data_in);

	i_data_len += i_len_tmp;

	if((unsigned int)i_data_len == p_st_packet->ui_payload_len)
	{
		if(i_direction == FR_DIRECT_CLIENT2SRV)
		{
			frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
			return APM_L7_IS_YES;
		}
		else
		{
			frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
			return APM_L7_IS_YES;
		}
	}

	return APM_L7_IS_NO;
}


/*!
 * dcom 私有数据创建
 *
 * \
 *
 * \return 			L7session_dcom_private* :	成功\n
 * 					NULL:	失败\n
 */
L7session_dcom_private* __dcom_private_create(void)
{
	L7session_dcom_private *p_st_private = NULL;

	p_st_private = (L7session_dcom_private*)frmalloc (sizeof(L7session_dcom_private));
	if(p_st_private == NULL)
		return NULL;

	memset(p_st_private, 0x00, sizeof(L7session_dcom_private));

	p_st_private->p_st_data = bytebuff_creat ();
	if(p_st_private->p_st_data == NULL)
	{
		frfree(p_st_private);
		return NULL;
	}

	p_st_private->i_packet_type = 0x0100;

	return p_st_private;

}

/*!
 * dcom request 数据解析
 *
 * \p_st_conn			(Conntrack *	连接信息)
 * \p_st_packet			(Apm_packet *	输入数据)
 * \p_st_hash_report	(Frhash_header *	上报链表)
 * \p_st_session		(L7_session *	session数据)
 *
 * \return				APM_L7_ANALYZE_FAIL :	失败\n
 * 						APM_L7_ANALYZE_TODO	:	数据包未结束\n
 * 						APM_L7_ANALYZE_SUCC	:	成功\n
 */
FR_STATIC int __dcom_req_analysis(Conntrack *p_st_conn, Apm_packet *p_st_packet
		, Frhash_header *p_st_hash_report, L7_session *p_st_session)
{
	int i_ret = 0;
	L7session_dcom_private *p_st_private = NULL;
	L4_transaction *p_st_l4_trans = NULL;
	Frreport_data *p_st_report_data = NULL;
	unsigned long ul_tmp = 0;

	if(p_st_conn == NULL || p_st_packet == NULL || p_st_hash_report == NULL || p_st_session == NULL)
		return APM_L7_ANALYZE_FAIL;

	//session 数据获取
	p_st_private = (L7session_dcom_private*)frct_get_session_private(p_st_session);
	if(p_st_private == NULL)
	{
		p_st_session->p_session_private = __dcom_private_create();
		if(p_st_session->p_session_private == NULL)
			return APM_L7_ANALYZE_FAIL;

		p_st_private = (L7session_dcom_private*)p_st_session->p_session_private;

		fruuid(p_st_private->arr_session_id);
	}
	p_st_l4_trans = &p_st_private->st_l4_trans;

	//first byte
	if(p_st_l4_trans->st_request_time_first_byte.tv_sec == 0)
	{
		//st_request_time_first_byte
		p_st_l4_trans->st_request_time_first_byte.tv_sec = p_st_packet->st_timestamp.tv_sec;
		p_st_l4_trans->st_request_time_first_byte.tv_usec = p_st_packet->st_timestamp.tv_usec;

		//seq
		p_st_private->ul_seq = frct_seq_increase(p_st_conn);

		//
		p_st_private->p_st_body_data_req = frreport_body_new_l7(p_st_conn);
		if(p_st_private->p_st_body_data_req == NULL)
			return APM_L7_ANALYZE_FAIL;
	}

	/* 请求的字节数 */
	p_st_l4_trans->l_request_bytes += p_st_packet->ui_payload_len;

	i_ret = bytebuff_add(p_st_private->p_st_data, p_st_packet->p_payload, p_st_packet->ui_payload_len);
	if(i_ret == FR_FAIL)
		return APM_L7_ANALYZE_FAIL;

	i_ret = dcom_parse(p_st_private->p_st_data, p_st_private, p_st_private->p_st_body_data_req, FR_YES);
	if(i_ret == APM_L7_ANALYZE_TODO)
		return APM_L7_ANALYZE_TODO;

	if(i_ret == APM_L7_ANALYZE_FAIL)
		return APM_L7_ANALYZE_FAIL;

	if(p_st_private->i_flag_report == 0)
	{
		bytebuff_free (&p_st_private->p_st_data);
		p_st_private->p_st_data = bytebuff_creat ();
		if(p_st_private->p_st_data == NULL)
			return APM_L7_ANALYZE_FAIL;

		return APM_L7_ANALYZE_SET_REQ(APM_L7_ANALYZE_SUCC);
	}

	//st_request_time
	p_st_l4_trans->st_request_time.tv_sec = p_st_packet->st_timestamp.tv_sec;
	p_st_l4_trans->st_request_time.tv_usec = p_st_packet->st_timestamp.tv_usec;

	//ct
	i_ret = frreport_body_l7_add_connInfo(p_st_private->p_st_body_data_req, p_st_conn);
	if(i_ret == FR_FAIL)
		return APM_L7_ANALYZE_FAIL;

	//信息上报
	if(frreport_body_l7_add_str(p_st_private->p_st_body_data_req, APM_REPORT_KEY_SESSION_ID, p_st_private->arr_session_id) == FR_FAIL)
		goto ERROR;

	if(frreport_body_l7_add_ulong(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_ID, p_st_private->ul_seq) == FR_FAIL)
		goto ERROR;

	if(frreport_body_l7_add_timeval(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_START_TIMESTAMP, &p_st_l4_trans->st_request_time_first_byte) == FR_FAIL)
		goto ERROR;

	if(frreport_body_l7_add_timeval(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_END_TIMESTAMP, &p_st_l4_trans->st_request_time) == FR_FAIL)
		goto ERROR;

	ul_tmp = frtime_diff(&p_st_l4_trans->st_request_time_first_byte, &p_st_l4_trans->st_request_time);
	if(frreport_body_l7_add_ulong(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_SEND_TIME_US, ul_tmp) == FR_FAIL)
		goto ERROR;

	if(frreport_body_l7_add_long(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_DATA_BYTES, p_st_l4_trans->l_request_bytes) == FR_FAIL)
		goto ERROR;

	//i_response_required APM_REPORT_KEY_REQ_ISRESPREQUIRED
	if(frreport_body_l7_add_int(p_st_private->p_st_body_data_req, APM_REPORT_KEY_REQ_ISRESPREQUIRED, 1) == FR_FAIL)
		goto ERROR;

	//body data
	p_st_report_data = frreport_data_l7_new(FRREPORT_TYPE_L7DATA_REQ);

	p_st_private->p_lst_comm_tmp = FRREPORT_BODY_L7LIST_CLONE_COMM(p_st_private->p_st_body_data_req);
	if(p_st_private->p_lst_comm_tmp == NULL)
		goto ERROR;

	p_st_private->p_lst_req_tmp = FRREPORT_BODY_L7LIST_CLONE_REQ(p_st_private->p_st_body_data_req);
	if(p_st_private->p_lst_req_tmp == NULL)
		goto ERROR;

	if(frreport_data_add_body(p_st_report_data, p_st_private->p_st_body_data_req) == FR_FAIL)
		goto ERROR;
	p_st_private->p_st_body_data_req = NULL;

	if(frreport_data_hash(p_st_hash_report, p_st_report_data) == FR_FAIL)
		goto ERROR;

	p_st_private->i_data_len = 0;
	p_st_private->i_packet_type = 0x0100;

	bytebuff_free (&p_st_private->p_st_data);
	p_st_private->p_st_data = bytebuff_creat ();
	if(p_st_private->p_st_data == NULL)
		return APM_L7_ANALYZE_FAIL;

	return APM_L7_ANALYZE_SET_REQ(APM_L7_ANALYZE_SUCC);

ERROR:

	if(p_st_report_data)
		frreport_data_free(&p_st_report_data);

	if(p_st_private->p_lst_comm_tmp)
		frreport_body_l7list_free(&p_st_private->p_lst_comm_tmp);

	if(p_st_private->p_lst_req_tmp)
		frreport_body_l7list_free(&p_st_private->p_lst_req_tmp);

	return APM_L7_ANALYZE_FAIL;

}

/*!
 * 私有数据初始化
 *
 * \p_st_private			(L7session_dcom_private *		私有数据)
 *
 * \return			FR_FAIL:失败\n
 * 					FR_SUCC:成功\n
 */
int __dcom_session_reset(L7session_dcom_private *p_st_private)
{
	if(p_st_private == NULL)
		return FR_FAIL;

	p_st_private->i_data_len = 0;
	p_st_private->i_packet_type = 0x0100;

	memset (&p_st_private->st_l4_trans, 0x00, sizeof (L4_transaction));

	if(p_st_private->p_st_data)
		bytebuff_free(&p_st_private->p_st_data);

	if(p_st_private->p_lst_comm_tmp)
		frreport_body_l7list_free(&p_st_private->p_lst_comm_tmp);

	if(p_st_private->p_lst_req_tmp)
		frreport_body_l7list_free(&p_st_private->p_lst_req_tmp);

	if(p_st_private->p_st_body_data_req)
		frreport_body_free(&p_st_private->p_st_body_data_req);

	if(p_st_private->p_st_body_data_resp)
		frreport_body_free(&p_st_private->p_st_body_data_resp);

	p_st_private->p_st_data = bytebuff_creat ();
	if(p_st_private->p_st_data == NULL)
		return FR_FAIL;

	return FR_SUCC;
}


/*!
 * dcom response 数据解析
 *
 * \p_st_conn			(Conntrack *	连接信息)
 * \p_st_packet			(Apm_packet *	输入数据)
 * \p_st_hash_report	(Frhash_header *	上报链表)
 * \p_st_session		(L7_session *	session数据)
 *
 * \return				APM_L7_ANALYZE_FAIL :	失败\n
 * 						APM_L7_ANALYZE_TODO	:	数据包未结束\n
 * 						APM_L7_ANALYZE_SUCC	:	成功\n
 */
FR_STATIC int __dcom_resp_analysis(Conntrack *p_st_conn, Apm_packet *p_st_packet
		, Frhash_header *p_st_hash_report, L7_session *p_st_session)
{
	int i_ret = 0;
	L7session_dcom_private *p_st_private = NULL;
	L4_transaction *p_st_l4_trans = NULL;
	Frreport_data *p_st_report_data = NULL;
	unsigned long ul_tmp = 0;

	if(p_st_conn == NULL || p_st_packet == NULL || p_st_hash_report == NULL || p_st_session == NULL)
		return APM_L7_ANALYZE_FAIL;

	//session 数据获取
	p_st_private = (L7session_dcom_private*)frct_get_session_private(p_st_session);
	if(p_st_private == NULL)
		return APM_L7_ANALYZE_FAIL;

	p_st_l4_trans = &p_st_private->st_l4_trans;

	//first byte
	if(p_st_l4_trans->st_response_time_first_byte.tv_sec == 0)
	{
		//st_response_time_first_byte
		p_st_l4_trans->st_response_time_first_byte.tv_sec = p_st_packet->st_timestamp.tv_sec;
		p_st_l4_trans->st_response_time_first_byte.tv_usec = p_st_packet->st_timestamp.tv_usec;

		//seq
		if(p_st_private->ul_seq == 0)
			p_st_private->ul_seq = frct_seq_increase(p_st_conn);

		//
		p_st_private->p_st_body_data_resp = frreport_body_new_l7(p_st_conn);
		if(p_st_private->p_st_body_data_resp == NULL)
			return APM_L7_ANALYZE_FAIL;

		//首包时间
		if(p_st_l4_trans->st_request_time_first_byte.tv_sec != 0)
			p_st_l4_trans->ul_response_time_firstbyte_us = frtime_diff(&(p_st_l4_trans->st_request_time_first_byte), &(p_st_l4_trans->st_response_time_first_byte));
		else
			p_st_l4_trans->ul_response_time_firstbyte_us = 0;
	}

	//如果没有req  直接返回
	if(p_st_private->p_lst_comm_tmp == NULL)
		return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);

	ul_tmp = frtime_diff(&p_st_l4_trans->st_request_time, &p_st_l4_trans->st_response_time_first_byte);
	if(frreport_body_l7_add_ulong(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_SERVER_TIME_US, ul_tmp) == FR_FAIL)
		goto ERROR;

	p_st_l4_trans->l_response_bytes += p_st_packet->ui_payload_len;

	i_ret = bytebuff_add(p_st_private->p_st_data, p_st_packet->p_payload, p_st_packet->ui_payload_len);
	if(i_ret == FR_FAIL)
		return APM_L7_ANALYZE_FAIL;

	i_ret = dcom_parse(p_st_private->p_st_data, p_st_private, p_st_private->p_st_body_data_resp, FR_YES);
	if(i_ret == APM_L7_ANALYZE_TODO)
		return APM_L7_ANALYZE_TODO;

	if(i_ret == APM_L7_ANALYZE_FAIL)
		return APM_L7_ANALYZE_FAIL;

	if(p_st_private->i_flag_report == 0)
	{
		if(__dcom_session_reset(p_st_private) == FR_FAIL)
			goto ERROR;

		return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);
	}

	//comm
	if(FRREPORT_BODY_L7LIST_SET_COMM(p_st_private->p_st_body_data_resp, &p_st_private->p_lst_comm_tmp) == FR_FAIL)
		goto ERROR;

	//把req数据链表添加到resp中
	if(FRREPORT_BODY_L7LIST_SET_REQ(p_st_private->p_st_body_data_resp, &p_st_private->p_lst_req_tmp) == FR_FAIL)
		goto ERROR;

	p_st_l4_trans->st_response_time.tv_sec = p_st_packet->st_timestamp.tv_sec;
	p_st_l4_trans->st_response_time.tv_usec = p_st_packet->st_timestamp.tv_usec;

	if(frreport_body_l7_add_timeval(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_START_TIMESTAMP, &p_st_l4_trans->st_response_time_first_byte) == FR_FAIL)
		goto ERROR;

	if(frreport_body_l7_add_timeval(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_END_TIMESTAMP, &p_st_l4_trans->st_response_time) == FR_FAIL)
		goto ERROR;

	ul_tmp = frtime_diff(&p_st_l4_trans->st_response_time_first_byte, &p_st_l4_trans->st_response_time);
	if(frreport_body_l7_add_ulong(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_DOWNLOAD_TIME_US, ul_tmp) == FR_FAIL)
		goto ERROR;

	if(frreport_body_l7_add_long(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_DATA_BYTES, p_st_l4_trans->l_response_bytes) == FR_FAIL)
		goto ERROR;

	if(frreport_body_l7_add_long(p_st_private->p_st_body_data_resp, APM_REPORT_KEY_RESP_CODE, 0) == FR_FAIL)
		goto ERROR;

	p_st_report_data = frreport_data_l7_new(FRREPORT_TYPE_L7DATA_RESP);
	if(p_st_report_data == NULL)
		goto ERROR;

	if(frreport_data_add_body(p_st_report_data, p_st_private->p_st_body_data_resp) == FR_FAIL)
		goto ERROR;
	p_st_private->p_st_body_data_resp = NULL;

	if(frreport_data_hash(p_st_hash_report, p_st_report_data) == FR_FAIL)
		goto ERROR;

	if(__dcom_session_reset(p_st_private) == FR_FAIL)
		goto ERROR;

	return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);


ERROR:
	if(p_st_report_data)
		frreport_data_free(&p_st_report_data);

	return APM_L7_ANALYZE_FAIL;

}

/*!
 * dcom 数据解析
 *
 * \p_st_conn			(Conntrack *	连接信息)
 * \p_st_packet			(Apm_packet *	输入数据)
 * \p_st_hash_report	(Frhash_header *	上报链表)
 * \p_st_session		(L7_session *	session数据)
 *
 * \return				APM_L7_ANALYZE_FAIL :	失败\n
 * 						APM_L7_ANALYZE_TODO	:	数据包未结束\n
 * 						APM_L7_ANALYZE_SUCC	:	成功\n
 */
int dcom_analysis(Conntrack *p_st_conn
		, Apm_packet *p_st_packet
		, Frhash_header *p_st_hash_report)
{
	int i_ret = 0;
	L7_session *p_st_session = NULL;

	if(p_st_conn == NULL || p_st_packet == NULL || p_st_hash_report == NULL)
		return APM_L7_ANALYZE_FAIL;

	p_st_session = frct_get_session(p_st_conn);
	if(p_st_session == NULL)
		return APM_L7_ANALYZE_FAIL;

	if(p_st_packet->em_analyz_cmd == FRANALYZ_CMD_FIN || p_st_packet->em_analyz_cmd == FRANALYZ_CMD_SYN)
	{
		dcom_session_destroy((void *)(p_st_session->p_session_private));
		p_st_session->p_session_private = NULL;
		return APM_L7_ANALYZE_SUCC;
	}

	//request
	if(p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
	{
		i_ret = __dcom_req_analysis(p_st_conn,p_st_packet,p_st_hash_report,p_st_session);
		if(i_ret == APM_L7_ANALYZE_FAIL)
			goto ERROR;

		return i_ret;
	}
	else//response
	{
		i_ret = __dcom_resp_analysis(p_st_conn,p_st_packet,p_st_hash_report, p_st_session);
		if (i_ret == APM_L7_ANALYZE_FAIL)
			goto ERROR;

		return i_ret;
	}

ERROR:

	dcom_session_destroy(p_st_session->p_session_private);
	p_st_session->p_session_private = NULL;

	return APM_L7_ANALYZE_FAIL;

}

/*!
 * 私有数据销毁
 *
 * \p_st_private			(void *		私有数据)
 *
 * \return			FR_FAIL:失败\n
 * 					FR_SUCC:成功\n
 */
int dcom_session_destroy(void *p_private_session)
{
	L7session_dcom_private *p_st_private = (L7session_dcom_private*)p_private_session;

	if(p_st_private)
	{
		if(p_st_private->p_st_data)
			bytebuff_free(&p_st_private->p_st_data);

		if(p_st_private->p_lst_comm_tmp)
			frreport_body_l7list_free(&p_st_private->p_lst_comm_tmp);

		if(p_st_private->p_lst_req_tmp)
			frreport_body_l7list_free(&p_st_private->p_lst_req_tmp);

		if(p_st_private->p_st_body_data_req)
			frreport_body_free(&p_st_private->p_st_body_data_req);

		if(p_st_private->p_st_body_data_resp)
			frreport_body_free(&p_st_private->p_st_body_data_resp);

		frfree(p_st_private);
	}

	return FR_SUCC;
}
